package com.baidu.automata.factory;

import java.io.File;
import java.lang.reflect.Field;
import java.util.Iterator;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.SAXValidator;
import org.dom4j.util.XMLErrorHandler;

import com.baidu.automata.core.context.Context;
import com.baidu.automata.core.flow.BaseOpStep;
import com.baidu.automata.core.flow.BaseOperation;
import com.baidu.automata.core.type.DataField;
import com.baidu.common.util.FieldUtil;
import com.baidu.config.OperationConfig;

public class OperationFactory {
	private String operationId = null;

	private String operationFilePath = null;

	private XMLErrorHandler errorHandler = new XMLErrorHandler();

	private SAXParser parser = null;

	private Document xmlDocument = null;

	private Context ctx = null;

	private BaseOperation op = null;

	private void setOperationFilePath() throws Exception {
		this.operationFilePath = OperationConfig.getInstance().getProperty(this.operationId);
	}

	private void setDocument() throws Exception {
		// 获取基于 SAX 的解析器的实例
		SAXParserFactory factory = SAXParserFactory.newInstance();
		// 解析器在解析时验证 XML 内容。
		factory.setValidating(true);
		// 指定由此代码生成的解析器将提供对 XML 名称空间的支持。
		factory.setNamespaceAware(true);
		// 使用当前配置的工厂参数创建 SAXParser 的一个新实例。
		parser = factory.newSAXParser();

		SAXReader xmlReader = new SAXReader();

		xmlDocument = (Document) xmlReader.read(new File(operationFilePath));
	}

	private void validatorXml() throws Exception {
		// 设置 XMLReader 的基础实现中的特定属性。
		parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");

		// 创建一个SAXValidator校验工具，并设置校验工具的属性
		SAXValidator validator = new SAXValidator(parser.getXMLReader());
		// 设置校验工具的错误处理器，当发生错误时，可以从处理器对象中得到错误信息。
		validator.setErrorHandler(errorHandler);
		// 校验
		validator.validate(xmlDocument);

		// 如果错误信息不为空，说明校验失败，打印错误信息
		if (errorHandler.getErrors().hasContent()) {
			throw new Exception(errorHandler.getErrors().asXML());
		}
	}

	@SuppressWarnings("unchecked")
	private void initCtx() throws Exception {
		Element ctxEmt = xmlDocument.getRootElement().element("context");
		if (ctxEmt == null) {
			return;
		}

		String ctxId = ctxEmt.attributeValue("id");
		String p = ctxEmt.attributeValue("parent");

		this.ctx = new Context(ctxId);

		for (Iterator<Element> it = ctxEmt.elementIterator(); it.hasNext();) {
			Element e = it.next();
			this.ctx.addDataField(FieldUtil.initFiled(e));
		}

		if (p != null && !"".equals(p)) {
			this.ctx.setParent(CtxFactory.getCtxById(p));
		}

	}

	@SuppressWarnings("unchecked")
	private void initOperation() throws Exception {
		Element opEmt = xmlDocument.getRootElement().element("operation");
		String opId = opEmt.attributeValue("id");
		String className = opEmt.attributeValue("class");
		String contextId = opEmt.attributeValue("context");

		if (className == null || "".equals(className)) {
			this.op = new BaseOperation(opId);
		} else {
			this.op = (BaseOperation) Class.forName(className).newInstance();
			this.op.setId(opId);
		}

		if (contextId != null && !"".equals(contextId)) {
			this.ctx = CtxFactory.getCtxById(contextId);
		}

		this.op.setCtx(ctx);

		// init steps
		for (Iterator<Element> it = opEmt.elementIterator(); it.hasNext();) {
			Element stepEmt = it.next();
			String stepId = stepEmt.attributeValue("id");
			String stepClassName = stepEmt.attributeValue("class");
			BaseOpStep step = (BaseOpStep) Class.forName(stepClassName).newInstance();
			step.setId(stepId);
			step.setCtx(ctx);

			// init args
			Element parasEmt = stepEmt.element("paras");
			if (parasEmt != null) {
				for (Iterator<Element> paraIt = parasEmt.elementIterator(); paraIt.hasNext();) {
					DataField f = FieldUtil.initFiled(paraIt.next());
					String fId = f.getId();
					Field field = step.getClass().getDeclaredField(fId);
					field.setAccessible(true);
					field.set(step, f);
				}
			}

			// init forwards
			Element forwardsEmt = stepEmt.element("forwards");
			for (Iterator<Element> forwardsIt = forwardsEmt.elementIterator(); forwardsIt.hasNext();) {
				Element fEmt = forwardsIt.next();
				String key = fEmt.attributeValue("key");
				String to = fEmt.attributeValue("to");
				step.addForward(key, to);
			}

			this.op.addOpStep(step);
		}
	}

	public static BaseOperation getOperationById(String id) throws Exception {
		OperationFactory opFactory = new OperationFactory();
		opFactory.operationId = id;

		opFactory.setOperationFilePath();
		opFactory.setDocument();
		opFactory.validatorXml();

		opFactory.initCtx();
		opFactory.initOperation();

		return opFactory.op;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			BaseOperation operation = getOperationById("test_operation");
			operation.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
